// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/child/web_url_loader_impl.h"

#include <stdint.h>
#include <string.h>

#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "content/child/request_extra_data.h"
#include "content/child/resource_dispatcher.h"
#include "content/child/sync_load_response.h"
#include "content/public/child/fixed_received_data.h"
#include "content/public/child/request_peer.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/resource_response_info.h"
#include "net/base/host_port_pair.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_util.h"
#include "net/url_request/redirect_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/WebURLError.h"
#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/platform/WebURLResponse.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace content {
namespace {

    const char kTestURL[] = "http://foo";
    const char kTestHTTPSURL[] = "https://foo";
    const char kTestData[] = "blah!";

    const char kFtpDirMimeType[] = "text/vnd.chromium.ftp-dir";
    // Simple FTP directory listing.  Tests are not concerned with correct parsing,
    // but rather correct cleanup when deleted while parsing.  Important details of
    // this list are that it contains more than one entry that are not "." or "..".
    const char kFtpDirListing[] = "drwxr-xr-x    3 ftp      ftp          4096 May 15 18:11 goat\n"
                                  "drwxr-xr-x    3 ftp      ftp          4096 May 15 18:11 hat";

    class TestResourceDispatcher : public ResourceDispatcher {
    public:
        TestResourceDispatcher()
            : ResourceDispatcher(nullptr, nullptr)
            , canceled_(false)
            , defers_loading_(false)
        {
        }

        ~TestResourceDispatcher() override { }

        // TestDispatcher implementation:

        void StartSync(std::unique_ptr<ResourceRequest> request,
            int routing_id,
            SyncLoadResponse* response,
            blink::WebURLRequest::LoadingIPCType ipc_type,
            mojom::URLLoaderFactory* url_loader_factory) override
        {
            *response = sync_load_response_;
        }

        int StartAsync(
            std::unique_ptr<ResourceRequest> request,
            int routing_id,
            scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner,
            const url::Origin& frame_origin,
            std::unique_ptr<RequestPeer> peer,
            blink::WebURLRequest::LoadingIPCType ipc_type,
            mojom::URLLoaderFactory* url_loader_factory,
            mojo::AssociatedGroup* associated_group) override
        {
            EXPECT_FALSE(peer_);
            EXPECT_EQ(blink::WebURLRequest::LoadingIPCType::ChromeIPC, ipc_type);
            peer_ = std::move(peer);
            url_ = request->url;
            stream_url_ = request->resource_body_stream_url;
            return 1;
        }

        void Cancel(int request_id) override
        {
            EXPECT_FALSE(canceled_);
            canceled_ = true;
        }

        RequestPeer* peer() { return peer_.get(); }

        bool canceled() { return canceled_; }

        const GURL& url() { return url_; }
        const GURL& stream_url() { return stream_url_; }

        void SetDefersLoading(int request_id, bool value) override
        {
            defers_loading_ = value;
        }
        bool defers_loading() const { return defers_loading_; }

        void set_sync_load_response(const SyncLoadResponse& sync_load_response)
        {
            sync_load_response_ = sync_load_response;
        }

    private:
        std::unique_ptr<RequestPeer> peer_;
        bool canceled_;
        bool defers_loading_;
        GURL url_;
        GURL stream_url_;
        SyncLoadResponse sync_load_response_;

        DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcher);
    };

    class TestWebURLLoaderClient : public blink::WebURLLoaderClient {
    public:
        TestWebURLLoaderClient(ResourceDispatcher* dispatcher)
            : loader_(new WebURLLoaderImpl(dispatcher, nullptr, nullptr))
            , delete_on_receive_redirect_(false)
            , delete_on_receive_response_(false)
            , delete_on_receive_data_(false)
            , delete_on_finish_(false)
            , delete_on_fail_(false)
            , did_receive_redirect_(false)
            , did_receive_response_(false)
            , check_redirect_request_priority_(false)
            , did_finish_(false)
        {
        }

        ~TestWebURLLoaderClient() override { }

        // blink::WebURLLoaderClient implementation:
        bool willFollowRedirect(
            blink::WebURLRequest& newRequest,
            const blink::WebURLResponse& redirectResponse) override
        {
            EXPECT_TRUE(loader_);

            if (check_redirect_request_priority_)
                EXPECT_EQ(redirect_request_priority, newRequest.getPriority());

            // No test currently simulates mutiple redirects.
            EXPECT_FALSE(did_receive_redirect_);
            did_receive_redirect_ = true;

            if (delete_on_receive_redirect_)
                loader_.reset();

            return true;
        }

        void didSendData(unsigned long long bytesSent,
            unsigned long long totalBytesToBeSent) override
        {
            EXPECT_TRUE(loader_);
        }

        void didReceiveResponse(
            const blink::WebURLResponse& response) override
        {
            EXPECT_TRUE(loader_);
            EXPECT_FALSE(did_receive_response_);

            did_receive_response_ = true;
            response_ = response;
            if (delete_on_receive_response_)
                loader_.reset();
        }

        void didDownloadData(int dataLength, int encodedDataLength) override
        {
            EXPECT_TRUE(loader_);
        }

        void didReceiveData(const char* data, int dataLength) override
        {
            EXPECT_TRUE(loader_);
            // The response should have started, but must not have finished, or failed.
            EXPECT_TRUE(did_receive_response_);
            EXPECT_FALSE(did_finish_);
            EXPECT_EQ(net::OK, error_.reason);
            EXPECT_EQ("", error_.domain.utf8());

            received_data_.append(data, dataLength);

            if (delete_on_receive_data_)
                loader_.reset();
        }

        void didFinishLoading(double finishTime,
            int64_t totalEncodedDataLength,
            int64_t totalEncodedBodyLength) override
        {
            EXPECT_TRUE(loader_);
            EXPECT_TRUE(did_receive_response_);
            EXPECT_FALSE(did_finish_);
            did_finish_ = true;

            if (delete_on_finish_)
                loader_.reset();
        }

        void didFail(const blink::WebURLError& error,
            int64_t totalEncodedDataLength,
            int64_t totalEncodedBodyLength) override
        {
            EXPECT_TRUE(loader_);
            EXPECT_FALSE(did_finish_);
            error_ = error;

            if (delete_on_fail_)
                loader_.reset();
        }

        WebURLLoaderImpl* loader() { return loader_.get(); }
        void DeleteLoader()
        {
            loader_.reset();
        }

        void set_delete_on_receive_redirect() { delete_on_receive_redirect_ = true; }
        void set_delete_on_receive_response() { delete_on_receive_response_ = true; }
        void set_delete_on_receive_data() { delete_on_receive_data_ = true; }
        void set_delete_on_finish() { delete_on_finish_ = true; }
        void set_delete_on_fail() { delete_on_fail_ = true; }
        void set_redirect_request_priority(blink::WebURLRequest::Priority priority)
        {
            check_redirect_request_priority_ = true;
            redirect_request_priority = priority;
        }

        bool did_receive_redirect() const { return did_receive_redirect_; }
        bool did_receive_response() const { return did_receive_response_; }
        const std::string& received_data() const { return received_data_; }
        bool did_finish() const { return did_finish_; }
        const blink::WebURLError& error() const { return error_; }
        const blink::WebURLResponse& response() const { return response_; }

    private:
        std::unique_ptr<WebURLLoaderImpl> loader_;

        bool delete_on_receive_redirect_;
        bool delete_on_receive_response_;
        bool delete_on_receive_data_;
        bool delete_on_finish_;
        bool delete_on_fail_;

        bool did_receive_redirect_;
        bool did_receive_response_;
        bool check_redirect_request_priority_;
        blink::WebURLRequest::Priority redirect_request_priority;
        std::string received_data_;
        bool did_finish_;
        blink::WebURLError error_;
        blink::WebURLResponse response_;

        DISALLOW_COPY_AND_ASSIGN(TestWebURLLoaderClient);
    };

    class WebURLLoaderImplTest : public testing::Test {
    public:
        WebURLLoaderImplTest()
        {
            client_.reset(new TestWebURLLoaderClient(&dispatcher_));
        }

        ~WebURLLoaderImplTest() override { }

        void DoStartAsyncRequest()
        {
            blink::WebURLRequest request { GURL(kTestURL) };
            request.setRequestContext(blink::WebURLRequest::RequestContextInternal);
            client()->loader()->loadAsynchronously(request, client());
            ASSERT_TRUE(peer());
        }

        void DoStartAsyncRequestWithPriority(
            blink::WebURLRequest::Priority priority)
        {
            blink::WebURLRequest request { GURL(kTestURL) };
            request.setRequestContext(blink::WebURLRequest::RequestContextInternal);
            request.setPriority(priority);
            client()->loader()->loadAsynchronously(request, client());
            ASSERT_TRUE(peer());
        }

        void DoReceiveRedirect()
        {
            EXPECT_FALSE(client()->did_receive_redirect());
            net::RedirectInfo redirect_info;
            redirect_info.status_code = 302;
            redirect_info.new_method = "GET";
            redirect_info.new_url = GURL(kTestURL);
            redirect_info.new_first_party_for_cookies = GURL(kTestURL);
            peer()->OnReceivedRedirect(redirect_info,
                content::ResourceResponseInfo());
            EXPECT_TRUE(client()->did_receive_redirect());
        }

        void DoReceiveHTTPSRedirect()
        {
            EXPECT_FALSE(client()->did_receive_redirect());
            net::RedirectInfo redirect_info;
            redirect_info.status_code = 302;
            redirect_info.new_method = "GET";
            redirect_info.new_url = GURL(kTestHTTPSURL);
            redirect_info.new_first_party_for_cookies = GURL(kTestHTTPSURL);
            peer()->OnReceivedRedirect(redirect_info,
                content::ResourceResponseInfo());
            EXPECT_TRUE(client()->did_receive_redirect());
        }

        void DoReceiveResponse()
        {
            EXPECT_FALSE(client()->did_receive_response());
            peer()->OnReceivedResponse(content::ResourceResponseInfo());
            EXPECT_TRUE(client()->did_receive_response());
        }

        // Assumes it is called only once for a request.
        void DoReceiveData()
        {
            EXPECT_EQ("", client()->received_data());
            auto size = strlen(kTestData);
            peer()->OnReceivedData(
                base::MakeUnique<FixedReceivedData>(kTestData, size));
            EXPECT_EQ(kTestData, client()->received_data());
        }

        void DoCompleteRequest()
        {
            EXPECT_FALSE(client()->did_finish());
            peer()->OnCompletedRequest(net::OK, false, false, base::TimeTicks(),
                strlen(kTestData), strlen(kTestData));
            EXPECT_TRUE(client()->did_finish());
            // There should be no error.
            EXPECT_EQ(net::OK, client()->error().reason);
            EXPECT_EQ("", client()->error().domain.utf8());
        }

        void DoFailRequest()
        {
            EXPECT_FALSE(client()->did_finish());
            peer()->OnCompletedRequest(net::ERR_FAILED, false, false, base::TimeTicks(),
                strlen(kTestData), strlen(kTestData));
            EXPECT_FALSE(client()->did_finish());
            EXPECT_EQ(net::ERR_FAILED, client()->error().reason);
            EXPECT_EQ(net::kErrorDomain, client()->error().domain.utf8());
        }

        void DoReceiveResponseFtp()
        {
            EXPECT_FALSE(client()->did_receive_response());
            content::ResourceResponseInfo response_info;
            response_info.mime_type = kFtpDirMimeType;
            peer()->OnReceivedResponse(response_info);
            EXPECT_TRUE(client()->did_receive_response());
        }

        void DoReceiveDataFtp()
        {
            auto size = strlen(kFtpDirListing);
            peer()->OnReceivedData(
                base::MakeUnique<FixedReceivedData>(kFtpDirListing, size));
            // The FTP delegate should modify the data the client sees.
            EXPECT_NE(kFtpDirListing, client()->received_data());
        }

        TestWebURLLoaderClient* client() { return client_.get(); }
        TestResourceDispatcher* dispatcher() { return &dispatcher_; }
        RequestPeer* peer() { return dispatcher()->peer(); }
        base::MessageLoop* message_loop() { return &message_loop_; }

    private:
        base::MessageLoop message_loop_;
        TestResourceDispatcher dispatcher_;
        std::unique_ptr<TestWebURLLoaderClient> client_;
    };

    TEST_F(WebURLLoaderImplTest, Success)
    {
        DoStartAsyncRequest();
        DoReceiveResponse();
        DoReceiveData();
        DoCompleteRequest();
        EXPECT_FALSE(dispatcher()->canceled());
        EXPECT_EQ(kTestData, client()->received_data());
    }

    TEST_F(WebURLLoaderImplTest, Redirect)
    {
        DoStartAsyncRequest();
        DoReceiveRedirect();
        DoReceiveResponse();
        DoReceiveData();
        DoCompleteRequest();
        EXPECT_FALSE(dispatcher()->canceled());
        EXPECT_EQ(kTestData, client()->received_data());
    }

    TEST_F(WebURLLoaderImplTest, RedirectCorrectPriority)
    {
        DoStartAsyncRequestWithPriority(
            blink::WebURLRequest::Priority::PriorityVeryHigh);
        client()->set_redirect_request_priority(
            blink::WebURLRequest::Priority::PriorityVeryHigh);
        DoReceiveRedirect();
        DoReceiveResponse();
        DoReceiveData();
        DoCompleteRequest();
        EXPECT_FALSE(dispatcher()->canceled());
        EXPECT_EQ(kTestData, client()->received_data());
    }

    TEST_F(WebURLLoaderImplTest, Failure)
    {
        DoStartAsyncRequest();
        DoReceiveResponse();
        DoReceiveData();
        DoFailRequest();
        EXPECT_FALSE(dispatcher()->canceled());
    }

    // The client may delete the WebURLLoader during any callback from the loader.
    // These tests make sure that doesn't result in a crash.
    TEST_F(WebURLLoaderImplTest, DeleteOnReceiveRedirect)
    {
        client()->set_delete_on_receive_redirect();
        DoStartAsyncRequest();
        DoReceiveRedirect();
    }

    TEST_F(WebURLLoaderImplTest, DeleteOnReceiveResponse)
    {
        client()->set_delete_on_receive_response();
        DoStartAsyncRequest();
        DoReceiveResponse();
    }

    TEST_F(WebURLLoaderImplTest, DeleteOnReceiveData)
    {
        client()->set_delete_on_receive_data();
        DoStartAsyncRequest();
        DoReceiveResponse();
        DoReceiveData();
    }

    TEST_F(WebURLLoaderImplTest, DeleteOnFinish)
    {
        client()->set_delete_on_finish();
        DoStartAsyncRequest();
        DoReceiveResponse();
        DoReceiveData();
        DoCompleteRequest();
    }

    TEST_F(WebURLLoaderImplTest, DeleteOnFail)
    {
        client()->set_delete_on_fail();
        DoStartAsyncRequest();
        DoReceiveResponse();
        DoReceiveData();
        DoFailRequest();
    }

    TEST_F(WebURLLoaderImplTest, DeleteBeforeResponseDataURL)
    {
        blink::WebURLRequest request(GURL("data:text/html;charset=utf-8,blah!"));
        client()->loader()->loadAsynchronously(request, client());
        client()->DeleteLoader();
        base::RunLoop().RunUntilIdle();
        EXPECT_FALSE(client()->did_receive_response());
    }

    // Data URL tests.

    TEST_F(WebURLLoaderImplTest, DataURL)
    {
        blink::WebURLRequest request(GURL("data:text/html;charset=utf-8,blah!"));
        client()->loader()->loadAsynchronously(request, client());
        base::RunLoop().RunUntilIdle();
        EXPECT_EQ("blah!", client()->received_data());
        EXPECT_TRUE(client()->did_finish());
        EXPECT_EQ(net::OK, client()->error().reason);
        EXPECT_EQ("", client()->error().domain.utf8());
    }

    TEST_F(WebURLLoaderImplTest, DataURLDeleteOnReceiveResponse)
    {
        blink::WebURLRequest request(GURL("data:text/html;charset=utf-8,blah!"));
        client()->set_delete_on_receive_response();
        client()->loader()->loadAsynchronously(request, client());
        base::RunLoop().RunUntilIdle();
        EXPECT_TRUE(client()->did_receive_response());
        EXPECT_EQ("", client()->received_data());
        EXPECT_FALSE(client()->did_finish());
    }

    TEST_F(WebURLLoaderImplTest, DataURLDeleteOnReceiveData)
    {
        blink::WebURLRequest request(GURL("data:text/html;charset=utf-8,blah!"));
        client()->set_delete_on_receive_data();
        client()->loader()->loadAsynchronously(request, client());
        base::RunLoop().RunUntilIdle();
        EXPECT_TRUE(client()->did_receive_response());
        EXPECT_EQ("blah!", client()->received_data());
        EXPECT_FALSE(client()->did_finish());
    }

    TEST_F(WebURLLoaderImplTest, DataURLDeleteOnFinish)
    {
        blink::WebURLRequest request(GURL("data:text/html;charset=utf-8,blah!"));
        client()->set_delete_on_finish();
        client()->loader()->loadAsynchronously(request, client());
        base::RunLoop().RunUntilIdle();
        EXPECT_TRUE(client()->did_receive_response());
        EXPECT_EQ("blah!", client()->received_data());
        EXPECT_TRUE(client()->did_finish());
    }

    TEST_F(WebURLLoaderImplTest, DataURLDefersLoading)
    {
        blink::WebURLRequest request(GURL("data:text/html;charset=utf-8,blah!"));
        client()->loader()->loadAsynchronously(request, client());

        // setDefersLoading() might be called with either false or true in no
        // specific order. The user of the API will not have sufficient information
        // about the WebURLLoader's internal state, so the latter gracefully needs to
        // handle calling setDefersLoading any number of times with any values from
        // any point in time.

        client()->loader()->setDefersLoading(false);
        client()->loader()->setDefersLoading(true);
        client()->loader()->setDefersLoading(true);
        base::RunLoop().RunUntilIdle();
        EXPECT_FALSE(client()->did_finish());

        client()->loader()->setDefersLoading(false);
        client()->loader()->setDefersLoading(true);
        base::RunLoop().RunUntilIdle();
        EXPECT_FALSE(client()->did_finish());

        client()->loader()->setDefersLoading(false);
        base::RunLoop().RunUntilIdle();
        EXPECT_TRUE(client()->did_finish());

        client()->loader()->setDefersLoading(true);
        client()->loader()->setDefersLoading(false);
        client()->loader()->setDefersLoading(false);
        base::RunLoop().RunUntilIdle();
        EXPECT_TRUE(client()->did_finish());

        EXPECT_EQ("blah!", client()->received_data());
        EXPECT_EQ(net::OK, client()->error().reason);
        EXPECT_EQ("", client()->error().domain.utf8());
    }

    TEST_F(WebURLLoaderImplTest, DefersLoadingBeforeStart)
    {
        client()->loader()->setDefersLoading(true);
        EXPECT_FALSE(dispatcher()->defers_loading());
        DoStartAsyncRequest();
        EXPECT_TRUE(dispatcher()->defers_loading());
    }

    // FTP integration tests.  These are focused more on safe deletion than correct
    // parsing of FTP responses.

    TEST_F(WebURLLoaderImplTest, Ftp)
    {
        DoStartAsyncRequest();
        DoReceiveResponseFtp();
        DoReceiveDataFtp();
        DoCompleteRequest();
        EXPECT_FALSE(dispatcher()->canceled());
    }

    TEST_F(WebURLLoaderImplTest, FtpDeleteOnReceiveResponse)
    {
        client()->set_delete_on_receive_response();
        DoStartAsyncRequest();
        DoReceiveResponseFtp();

        // No data should have been received.
        EXPECT_EQ("", client()->received_data());
    }

    TEST_F(WebURLLoaderImplTest, FtpDeleteOnReceiveFirstData)
    {
        client()->set_delete_on_receive_data();
        DoStartAsyncRequest();
        DoReceiveResponseFtp();

        EXPECT_NE("", client()->received_data());
    }

    TEST_F(WebURLLoaderImplTest, FtpDeleteOnReceiveMoreData)
    {
        DoStartAsyncRequest();
        DoReceiveResponseFtp();
        DoReceiveDataFtp();

        // Directory listings are only parsed once the request completes, so this will
        // cancel in DoReceiveDataFtp, before the request finishes.
        client()->set_delete_on_receive_data();
        peer()->OnCompletedRequest(net::OK, false, false, base::TimeTicks(),
            strlen(kTestData), strlen(kTestData));
        EXPECT_FALSE(client()->did_finish());
    }

    TEST_F(WebURLLoaderImplTest, FtpDeleteOnFinish)
    {
        client()->set_delete_on_finish();
        DoStartAsyncRequest();
        DoReceiveResponseFtp();
        DoReceiveDataFtp();
        DoCompleteRequest();
    }

    TEST_F(WebURLLoaderImplTest, FtpDeleteOnFail)
    {
        client()->set_delete_on_fail();
        DoStartAsyncRequest();
        DoReceiveResponseFtp();
        DoReceiveDataFtp();
        DoFailRequest();
    }

    // PlzNavigate: checks that the stream override parameters provided on
    // navigation commit are properly applied.
    TEST_F(WebURLLoaderImplTest, BrowserSideNavigationCommit)
    {
        // Initialize the request and the stream override.
        const GURL kNavigationURL = GURL(kTestURL);
        const GURL kStreamURL = GURL("http://bar");
        const std::string kMimeType = "text/html";
        blink::WebURLRequest request(kNavigationURL);
        request.setFrameType(blink::WebURLRequest::FrameTypeTopLevel);
        request.setRequestContext(blink::WebURLRequest::RequestContextFrame);
        std::unique_ptr<StreamOverrideParameters> stream_override(
            new StreamOverrideParameters());
        stream_override->stream_url = kStreamURL;
        stream_override->response.mime_type = kMimeType;
        RequestExtraData* extra_data = new RequestExtraData();
        extra_data->set_stream_override(std::move(stream_override));
        request.setExtraData(extra_data);
        base::CommandLine::ForCurrentProcess()->AppendSwitch(
            switches::kEnableBrowserSideNavigation);

        client()->loader()->loadAsynchronously(request, client());

        // The stream url should have been added to the ResourceRequest.
        ASSERT_TRUE(peer());
        EXPECT_EQ(kNavigationURL, dispatcher()->url());
        EXPECT_EQ(kStreamURL, dispatcher()->stream_url());

        EXPECT_FALSE(client()->did_receive_response());
        peer()->OnReceivedResponse(content::ResourceResponseInfo());
        EXPECT_TRUE(client()->did_receive_response());

        // The response info should have been overriden.
        ASSERT_FALSE(client()->response().isNull());
        EXPECT_EQ(kMimeType, client()->response().mimeType().latin1());

        DoReceiveData();
        DoCompleteRequest();
        EXPECT_FALSE(dispatcher()->canceled());
        EXPECT_EQ(kTestData, client()->received_data());
    }

    TEST_F(WebURLLoaderImplTest, ResponseIPAddress)
    {
        GURL url("http://example.test/");

        struct TestCase {
            const char* ip;
            const char* expected;
        } cases[] = {
            { "127.0.0.1", "127.0.0.1" },
            { "123.123.123.123", "123.123.123.123" },
            { "::1", "[::1]" },
            { "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
                "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]" },
            { "2001:db8:85a3:0:0:8a2e:370:7334", "[2001:db8:85a3:0:0:8a2e:370:7334]" },
            { "2001:db8:85a3::8a2e:370:7334", "[2001:db8:85a3::8a2e:370:7334]" },
            { "::ffff:192.0.2.128", "[::ffff:192.0.2.128]" }
        };

        for (const auto& test : cases) {
            SCOPED_TRACE(test.ip);
            content::ResourceResponseInfo info;
            info.socket_address = net::HostPortPair(test.ip, 443);
            blink::WebURLResponse response;
            WebURLLoaderImpl::PopulateURLResponse(url, info, &response, true);
            EXPECT_EQ(test.expected, response.remoteIPAddress().utf8());
        };
    }

    // Verifies that the lengths used by the PerformanceResourceTiming API are
    // correctly assigned for sync XHR.
    TEST_F(WebURLLoaderImplTest, SyncLengths)
    {
        static const char kBodyData[] = "Today is Thursday";
        const int kEncodedBodyLength = 30;
        const int kEncodedDataLength = 130;
        const GURL url(kTestURL);
        blink::WebURLRequest request(url);
        request.setRequestContext(blink::WebURLRequest::RequestContextInternal);

        // Prepare a mock response
        SyncLoadResponse sync_load_response;
        sync_load_response.error_code = net::OK;
        sync_load_response.url = url;
        sync_load_response.data = kBodyData;
        ASSERT_EQ(17u, sync_load_response.data.size());
        sync_load_response.encoded_body_length = kEncodedBodyLength;
        sync_load_response.encoded_data_length = kEncodedDataLength;
        dispatcher()->set_sync_load_response(sync_load_response);

        blink::WebURLResponse response;
        blink::WebURLError error;
        blink::WebData data;
        int64_t encoded_data_length = 0;
        int64_t encoded_body_length = 0;
        client()->loader()->loadSynchronously(
            request, response, error, data, encoded_data_length, encoded_body_length);

        EXPECT_EQ(kEncodedBodyLength, encoded_body_length);
        EXPECT_EQ(kEncodedDataLength, encoded_data_length);
    }

} // namespace
} // namespace content
